package net.cyue.web.easyquery.core.config;

import net.cyue.util.ReflectUtil;
import net.cyue.web.easyquery.core.db.QueryType;
import net.cyue.web.easyquery.core.db.SQLType;
import net.cyue.web.easyquery.core.http.HTTPRequestMethod;
import net.cyue.web.easyquery.core.http.data.QueryParameter;
import net.cyue.web.easyquery.core.http.handler.api.IWebRequestPreHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

public class ConfigItemValue {

    protected static final String DEFAULT_PAGE_PARAMETER = "page";
    protected static final String DEFAULT_PAGE_SIZE_PARAMETER = "pageSize";
    protected static final int DEFAULT_PAGE_SIZE = 10;

    protected static final Logger LOGGER = LoggerFactory.getLogger(ConfigItemValue.class);

    public enum Type {
        ADDITIONAL,
        API
    }

    public static class Additional extends ConfigItemValue {
        public String group;
        public String name;
        public String value;

        public String getGroup() {
            return this.group == null ? this.name : this.group;
        }
        public String getName() {
            return this.name;
        }
        public String getValue() {
            return this.value;
        }
        public String getKey() {
            return this.group + "." + this.name;
        }
        public void setKey(String key) {
            if (key == null) {
                return;
            }
            int index = key.lastIndexOf(".");
            if (index >= 0) {
                this.group = key.substring(0, index);
                this.name = key.substring(index + ".".length());
            } else {
                this.group = null;
                this.name = key;
            }
        }

        public AdditionalConfigItem toAdditionalConfigItem() {
            String[] groupNames = this.group.split("\\.");
            AdditionalConfigGroup group = null;
            for (String groupName : groupNames) {
                group = AdditionalConfigGroup.create(group, groupName);
            }
            return AdditionalConfigItem.create(group, this.getName(), this.description);
        }
    }

    public static class API extends ConfigItemValue {
        private final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        public HTTPRequestMethod method = HTTPRequestMethod.GET;
        private IWebRequestPreHandler preHandler;
        public String[] paths;
        public String sql;
        public SQLType sqlType = SQLType.UNKNOWN;
        public QueryType queryType = QueryType.QUERY_LIST;
        public String pageParameter = DEFAULT_PAGE_PARAMETER;
        public String pageSizeParameter = DEFAULT_PAGE_SIZE_PARAMETER;
        protected final Set<QueryParameter> queryParameterSet = new HashSet<>();

        public boolean addQueryParameter(QueryParameter queryParameter) {
            if (queryParameter == null) {
                return false;
            }
            return this.queryParameterSet.add(queryParameter);
        }
        public boolean addQueryParameters(QueryParameter... queryParameters) {
            if (queryParameters == null || queryParameters.length == 0) {
                return false;
            }
            return this.queryParameterSet.addAll(Arrays.asList(queryParameters));
        }
        public boolean addQueryParameterList(List<QueryParameter> list) {
            if (list == null || list.isEmpty()) {
                return false;
            }
            return this.queryParameterSet.addAll(list);
        }
        public boolean removeQueryParameter(QueryParameter queryParameter) {
            return this.queryParameterSet.remove(queryParameter);
        }

        public void setPreHandler(IWebRequestPreHandler preHandler) {
            this.preHandler = preHandler;
        }
        public void setPreHandler(String className) {
            try {
                Class<?> clz = this.classLoader.loadClass(className);;
                if (!IWebRequestPreHandler.class.isAssignableFrom(clz)) {
                    LOGGER.warn("Invalid PreHandler class: {}", className);
                    return;
                }
                this.preHandler = (IWebRequestPreHandler) ReflectUtil.createInstance(clz);
            } catch (ClassNotFoundException | NoSuchMethodException e) {
                LOGGER.warn(e.getMessage(), e);
            }
        }

        public HTTPRequestMethod getMethod() {
            return this.method;
        }
        public IWebRequestPreHandler getPreHandler() {
            return this.preHandler;
        }
        public String[] getPaths() {
            return this.paths;
        }
        public Set<QueryParameter> getQueryParameterSet() {
            return this.queryParameterSet;
        }
        public QueryParameter[] getQueryParameters() {
            return this.queryParameterSet.toArray(new QueryParameter[0]);
        }
        public List<QueryParameter> getQueryParameterList() {
            return new ArrayList<>(this.queryParameterSet);
        }
        public String[] getQueryParameterNames() {
            return this.queryParameterSet.stream().map(QueryParameter::getName).toArray(String[]::new);
        }
        public List<String> getQueryParameterNameList() {
            return this.queryParameterSet.stream().map(QueryParameter::getName).collect(Collectors.toList());
        }
        public String getSQL() {
            return this.sql;
        }
        public SQLType getSQLType() {
            return this.sqlType;
        }
        public QueryType getQueryType() {
            return this.queryType;
        }
        public String getPageParameter() {
            return this.pageParameter;
        }
        public String getPageSizeParameter() {
            return this.pageSizeParameter;
        }
    }

    public Type type;
    public String description;

    public Type getType() {
        return this.type;
    }
    public String getDescription() {
        return this.description;
    }

    public static Additional fromKeyValue(String key, String value) {
        Additional additional = new Additional();
        additional.type = Type.ADDITIONAL;
        additional.value = value;
        additional.setKey(key);
        return additional;
    }
}
